import { GraphConfigData } from "@logicflow/core";
import MyContext from "./MyContext";
import ELNode from "./type/ELNode";
import { ELType, GroupType } from "./type/ELType";
import Queue from "./type/Queue";
interface SingleNodeParseConfig {
    // 是否在入度大于1的时候终止
    StopOnMultipleInput: boolean,
    ingoreInNum?: boolean // 为true时覆盖inNumStop的设置，无视inNum
    targetNode?: ELNode
}

export default class MyParse {
    private logicFlow: GraphConfigData;

    private context: MyContext;

    constructor(logicFlow: GraphConfigData) {
        this.logicFlow = logicFlow;
        this.context = new MyContext();
        this.context.init(logicFlow);
    }

    public parse(): ELNode {
        const startId = this.context.startId;
        const node = new ELNode();
        node.id = startId;
        this.parseThenChain(node);
        return this.context.pop();
    }

    /**
     * 解析一条链
     * @param node 这条链的开始节点 
     * @param defaultData 链前面的一些节点，这些节点是并行的
     * @returns 
     */
    private parseThenChain(node: ELNode, defaultData: ELNode[] = []): ELNode {
        this.context.createStackEnv({ type: ELType.THEN } as ELNode);

        if (defaultData && defaultData.length > 0) {
            this.context.createStackEnv({ type: ELType.WHEN } as ELNode);
            defaultData.forEach(data => {
                this.context.elStack.push(data);
            })
            this.context.quitStackEnv()
        }

        this.parseSingleNode(node, { StopOnMultipleInput: false });
        return this.context.quitStackEnv();
    }

    /**
     * 分析两个节点之间的链，仅用于switch
     * @param s 开始节点 
     * @param t 目标节点 
     * @returns 
     */
    private praseBetween(s: ELNode, t: ELNode) {
        this.context.createStackEnv({ type: ELType.THEN } as ELNode);

        this.parseSingleNode(s, { StopOnMultipleInput: false, ingoreInNum: true, targetNode: t });
        return this.context.quitStackEnv();

    }

    /**
     * 分析单个的then节点
     *
     * @param node    节点id
     * @param isStart 是否是then链的起点
     * @returns
     */
    private parseSingleNode(node: ELNode, config: SingleNodeParseConfig) {
        const id = node.id;
        const inNum = this.context.getSourceNum(node);
        const outNum = this.context.getEndNum(node);
        const newConfig: SingleNodeParseConfig = {
            ingoreInNum: config.ingoreInNum,
            targetNode: config.targetNode,
            StopOnMultipleInput: false
        }

        if (this.context.isEnd(id)) {
            this.context.setStackEndPoint(node);
            return null;
        }
        if (config.targetNode && config.targetNode.id === node.id) {
            this.context.setStackEndPoint(node);
            return null;
        }

        // 这时候一定是在when分支中
        if (inNum > 1 && !config.ingoreInNum && config.StopOnMultipleInput) {
            this.context.setStackEndPoint(node);
            return null;
        }

        // 虚拟节点不展示,其他节点入栈
        if (!this.context.isStart(id)) this.context.push(node);

        //先分析特殊节点
        if (ELType.IF === node.type) {
            const next = this.parseIF(this.context.pop());
            newConfig.StopOnMultipleInput = (this.context.getSourceNum(next) === 0);
            return this.parseSingleNode(next, newConfig);
        }
        if (ELType.SWITCH === node.type) {
            const next = this.parseWhich(this.context.pop());
            newConfig.StopOnMultipleInput = (this.context.getSourceNum(next) === 0)
            return this.parseSingleNode(next, newConfig);
        }
        //需要考虑与或非表达式作为if起点的情况
        if (ELType.GROUP === node.type) {
            let next = this.parseGroup(this.context.pop());
            if (node.groupType === GroupType.LOGIC && outNum > 0 && !this.context.isEnd(this.context.getEndList(node)[0].id)) {
                const logicNode = this.context.pop();
                next = this.parseIF(logicNode);
            }
            newConfig.StopOnMultipleInput = (this.context.getSourceNum(next) === 0)
            return this.parseSingleNode(next, newConfig);
        }

        //普通节点
        if (outNum === 1) {
            newConfig.StopOnMultipleInput = true;
            return this.parseSingleNode(this.context.getEndList(node)[0], newConfig);
        }
        //否则为when节点
        const next = this.parseWhenChain(node);
        newConfig.StopOnMultipleInput = (this.context.getSourceNum(next) === 0);
        return this.parseSingleNode(next, newConfig);
    }

    /**
     * 解析When节点
     *
     * @param node when的开始节点
     * @returns when的结束节点，便于继续分析
     */
    private parseWhenChain(node: ELNode) {
        this.context.createStackEnv({ type: ELType.WHEN } as ELNode);

        const end = this.parseBranch(node);

        this.context.setStackEndPoint(end);

        return this.context.quitStackEnv();
    }

    /**
     * 将各个分支依次压入栈中
     *
     * @param node 分支收束的节点
     */
    private parseBranch(node: ELNode): ELNode {
        const nodeId = node.id;
        //分支起点的出度，用于判断何时结束
        let outNum = this.context.getEndNum(node);

        const queue = new Queue<ELNode>();
        // 每一个收束节点对应的Then链数组
        const endData: Record<string, ELNode[]> = {};
        this.context.endPoints[nodeId].forEach(startNode => {
            const startId = startNode.id;
            //将空链作为已分析完的链,取出时需要判空
            if (this.context.getSourceNum(startNode) > 1) {
                const endId = startId
                this.pushToEndData(endData, endId, null);
            }
            queue.push(startNode);
        })

        while (outNum > 0) {
            const start = queue.pop();
            if (!start) throw new Error();
            // 当endData[start.id]非空，说明它其实是一个提前收束的节点
            const end = this.parseThenChain(start, endData[start.id]);
            this.pushToEndData(endData, end.id, this.context.elStack.pop());

            //当入度出度匹配时，循环退出
            const endSize = endData[end.id].length;
            if (outNum === endSize) {
                queue.push(end);
                break;
            }

            //有节点提前收束
            if (endSize === this.context.getSourceNum(end)) {
                outNum -= endSize - 1;
                queue.push(end);
            }
        }
        const end = queue.pop();
        if (!end) throw new Error();
        endData[end.id].forEach(node1 => {
            if (!node1) return;
            this.context.push(node1);
        })

        const endSize = endData[end.id].length;
        const sourceNum = this.context.getSourceNum(end);
        this.context.setSourceNum(end, sourceNum - endSize + 1);
        return end;
    }

    private pushToEndData(endData: Record<string, ELNode[]>, endId: string, node: ELNode) {
        if (!endData[endId]) endData[endId] = [];
        endData[endId].push(node);
    }

    private parseIF(node: ELNode) {
        const typenode = new ELNode()
        typenode.type = ELType.IF;
        this.context.createStackEnv(typenode);

        if (node.type === ELType.IF) node.type = ELType.ID;
        this.context.push(node);
        //if节点必然有两个分支，但是可能有undefined
        const ends = this.context.getEndList(node).filter(n => !!n);
        const outNum = ends.length;

        // 当if节点分支数为1，必须在末尾
        if (outNum == 1) {
            let child: ELNode;
            let end: ELNode;
            try {
                child = ends[0];
                end = this.context.endPoints[child.id][0];
                if (!this.context.isEnd(end.id)) throw new Error();
            } catch (err) {
                throw new Error("IF 判断节点的分支数必须为2");
            }

            this.context.push(child);
            return end;
        }
        if (outNum != 2) throw new Error("IF 判断节点的分支数必须为2");

        const end = this.parseBranch(node);
        this.context.setStackEndPoint(end);
        return this.context.quitStackEnv();
    }

    /**
     * 获取分支的结束节点 ,原理就是获取每个分支都能走到的节点
     */
    private getBranchEnd(node: ELNode): ELNode {
        //分支起点的出度，用于判断何时结束
        let outNum = this.context.getEndNum(node);

        const nodes_id = this.context.getEndList(node).map(n => n.id)
        // 节点的访问次数
        const visited_count: Record<string, number> = {}
        while (true) {
            for (let i = 0; i < nodes_id.length; i++) {
                const id = nodes_id[i]
                if (!id) continue
                const n = this.context.getNodeById(id)
                //更新访问次数
                if (!visited_count[n.id]) visited_count[n.id] = 0
                visited_count[n.id]++
                //访问次数达标，返回该节点
                if (visited_count[n.id] === outNum) {
                    return n
                }

                if (this.context.isEnd(n.id)) {
                    nodes_id[i] = ""
                } else if (n.type === ELType.SWITCH) {
                    nodes_id[i] = this.getBranchEnd(n).id
                } else {
                    nodes_id[i] = this.context.getEndList(n)[0].id
                }
            }
        }
    }

    /**
     * 解析Switch节点，这里不应该使用parseBranch，每一个后继节点都应该看成单独的一条链
     * @param node type为Switch的节点
     * @returns 
     */
    private parseWhich(node: ELNode): ELNode {
        const follows = this.context.getEndList(node);
        const outNum = follows.length;
        if (outNum <= 1) throw new Error("WHICH 分支节点的分支数必须大于1");

        // 先获取Switch的结束节点，即swhich解析完的下一个解析节点
        const next = this.getBranchEnd(node)
        debugger

        // 下面开始正式的解析
        this.context.createStackEnv(node);
        follows.forEach(start => {
            this.praseBetween(start, next);
        })
        this.context.setStackEndPoint(next);
        return this.context.quitStackEnv();
    }


    /**
     * 解析group
     * @param node 
     */
    private parseGroup(node: ELNode): ELNode {
        const ends = this.context.getEndList(node);

        let res: ELNode;
        let toEnd: boolean = true;//是否直接连接到end，即没有后继节点

        ends.forEach(end => {
            if (end.comingEdgeText) {
                this.parseThenChain(end);
                const then = this.context.elStack.pop();
                switch (end.comingEdgeText) {
                    case "START":
                        node.startNode = then.child[0];
                        break;
                    case "BREAK":
                        node.breakNode = then.child[0];
                        break;
                    case "EXCEPTION":
                        node.exceptionNode = then.child[0];
                        break;
                }
            } else {
                res = end;
                toEnd = false;
            }
        })
        this.context.elStack.push(node);
        if (toEnd) {
            return new ELNode(this.context.endId)
        }
        return res!;
    }


}